package weeny.pre.control;

import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;

import prefuse.Display;
import prefuse.data.Edge;
import prefuse.visual.VisualItem;

import weeny.pre.control.MoreDragControl.DragListener;
import weeny.pre.util.Geom;
import weeny.pre.util.Pre;

/**
 * 
 * When you drag a item, if the item has a valid edgeitem in a field passed in
 * as a parameter, we make item can only be moved along the edge.
 * 
 * However, when you drag an end node of an edge, the item on the edge does not
 * move accordingly, sadly.
 * 
 * @author weeny
 */
public class DragAlongEdgeListener extends DragListener{
	private Point2D tmpSrc = new Point2D.Double();
	private Point2D tmpTar = new Point2D.Double();
	private Point2D tmpMse = new Point2D.Double();
	private String edgeField = null;
	private String group;
	
	/**
	 * The drag adjustment is performed on every item in this group. And you
	 * should specify the column (with type {@link Edge}), in which stores the
	 * edge the item is sliding on.
	 */
	public DragAlongEdgeListener(String group, String edgeColumn){
		this.group = group;
		this.edgeField = edgeColumn;
	}
	private boolean isValidItem(VisualItem item){
		if (item.getVisualization().getGroup(group) == null) {
			return false;
		}
		else if (!item.canGet(edgeField, Edge.class)) {
			return false;
		}
		else if (!item.getVisualization().getGroup(group).containsTuple(item)){
			return false;
		}
		else {
			return true;	
		}
	}
	@Override
	public void afterItemDragged(VisualItem item, MouseEvent e) {
		if (!isValidItem(item)) {
			return;
		}
		Edge edge = (Edge) item.get(edgeField);
		double newX = item.getX();
		double newY = item.getY();
		double dx = item.getX()-item.getStartX();
		double dy = item.getY()-item.getStartY();

		if ((dx == 0 && dy == 0) || edge == null) {
			return;
		}
		((Display)e.getSource()).getAbsoluteCoordinate(e.getPoint(), tmpMse);
		if(Math.abs(Geom.deltaX(edge))>Math.abs(Geom.deltaY(edge))){
			//width is bigger than height, so left point is source and right
			//point is target
			if (Geom.deltaX(edge)>0) {
				Geom.dumpPnt(edge.getSourceNode(), tmpSrc);
				Geom.dumpPnt(edge.getTargetNode(), tmpTar);
			}
			else{
				Geom.dumpPnt(edge.getSourceNode(), tmpTar);
				Geom.dumpPnt(edge.getTargetNode(), tmpSrc);
			}
			newX = tmpMse.getX()>tmpTar.getX()? tmpTar.getX() : newX;
			newX = tmpMse.getX()<tmpSrc.getX()? tmpSrc.getX() : newX;
			if (newX >= tmpTar.getX()) {
				Pre.setPos(item, tmpTar);
			}
			else if (newX <= tmpSrc.getX()) {
				Pre.setPos(item, tmpSrc);
			}
			else{
				double fracX = Geom.fracXBetween(tmpSrc, item, tmpTar);
				newY = tmpSrc.getY()+fracX*(tmpTar.getY()-tmpSrc.getY());
				Pre.setPos(item, newX, newY);
			}
		}
		else{
			//height is bigger than width, so top point is source and buttom
			//point is target
			if (Geom.deltaY(edge)>0) {
				Geom.dumpPnt(edge.getSourceNode(), tmpSrc);
				Geom.dumpPnt(edge.getTargetNode(), tmpTar);
			}
			else{
				Geom.dumpPnt(edge.getSourceNode(), tmpTar);
				Geom.dumpPnt(edge.getTargetNode(), tmpSrc);
			}
			newY = tmpMse.getY()>tmpTar.getY()? tmpTar.getY() : newY;
			newY = tmpMse.getY()<tmpSrc.getY()? tmpSrc.getY() : newY;
			if (newY >= tmpTar.getY()) {
				Pre.setPos(item, tmpTar);
			}
			else if (newY <= tmpSrc.getY()) {
				Pre.setPos(item, tmpSrc);
			}
			else{
				double fracY = Geom.fracYBetween(tmpSrc, item, tmpTar);
				newX = tmpSrc.getX()+fracY*(tmpTar.getX()-tmpSrc.getX());
				Pre.setPos(item, newX, item.getY());
			}
		}

	}
}